#include "stdafx.h"
#include "sgllib.h"
#include "sglframe.h"
#include "zlib.h"

SglFrame::SglFrame(LPCTSTR pszFile, DWORD dwOffset) :
	m_Error(SGL_SUCCESS),
	m_FilePath(pszFile),
	m_Offset(dwOffset)
{
	ASSERT(pszFile && dwOffset);
	memset(&m_Buffer, 0, sizeof(m_Buffer));
}

SglFrame::~SglFrame()
{

}

INT SglFrame::GetError(VOID) const
{
	return m_Error;
}

const SglFrame::BUFFER *SglFrame::GetBuffer(VOID)
{
	return &m_Buffer;
}

BOOL SglFrame::Decode(VOID)
{
	if (m_Buffer.pBlock)
	{
		m_Error = SGL_ALREADYDECODED;
		return FALSE;
	}

	if (!m_FilePath || !*m_FilePath || !m_Offset)
	{
		m_Error = SGL_INVALIDHANDLE;
		return FALSE;
	}

	FILE *pFile = _tfopen(m_FilePath, _T("rb"));
	if (!pFile)
	{
		m_Error = SGL_ACCESSDENIED;
		return FALSE;
	}

	BOOL bRet = DecodeFrame(pFile);

	fclose(pFile);

	if (!bRet)
	{
		ClearBuffer();
		return FALSE;
	}

	m_Error = SGL_SUCCESS;
	return TRUE;
}

BOOL SglFrame::Release(VOID)
{
	if (!ClearBuffer())
	{
		m_Error = SGL_NOTDECODEDYET;
		return FALSE;
	}

	m_Error = SGL_SUCCESS;
	return TRUE;
}

BOOL SglFrame::DecodeFrame(FILE *pFile)
{
	ASSERT(pFile);

	if (fseek(pFile, m_Offset, SEEK_SET))
	{
		m_Error = SGL_BADFORMAT;
		return FALSE;
	}

	SGL_FRAMEHEADER sHeader;
	if (!fread(&sHeader, sizeof(sHeader), 1, pFile))
	{
		m_Error = SGL_BADFORMAT;
		return FALSE;
	}

	if (!sHeader.wWidth || !sHeader.wHeight)
	{
		if (m_Buffer.wDataWidth || m_Buffer.wDataHeight || m_Buffer.wHBlockNum || m_Buffer.wVBlockNum)
		{
			TMP_ASSERT(FALSE);
			m_Error = SGL_BADFORMAT;
			return FALSE;
		}
	}

	m_Buffer.wWidth = sHeader.wWidth;
	m_Buffer.wHeight = sHeader.wHeight;
	m_Buffer.wDataWidth = sHeader.wDataWidth;
	m_Buffer.wDataHeight = sHeader.wDataHeight;
	m_Buffer.shHOffset = sHeader.shHOffset;
	m_Buffer.shVOffset = sHeader.shVOffset;
	m_Buffer.wHBlockNum = sHeader.wHBlockNum;
	m_Buffer.wVBlockNum = sHeader.wVBlockNum;

	TMP_ASSERT(!sHeader.wUnknown1);
	TMP_ASSERT(!sHeader.wUnknown2);

	DWORD dwBlockNum = m_Buffer.wHBlockNum * m_Buffer.wVBlockNum;
	if (!dwBlockNum)
		return TRUE;

	m_Buffer.pBlock = new BLOCK[dwBlockNum];
	if (!m_Buffer.pBlock)
	{
		m_Error = SGL_OUTOFMEMORY;
		return FALSE;
	}
	memset(m_Buffer.pBlock, 0, sizeof(BLOCK) * dwBlockNum);

	for (UINT i = 0U; i < dwBlockNum; i++)
	{
		if (!DecodeBlock(pFile, &m_Buffer.pBlock[i]))
			return FALSE;
	}

	return TRUE;
}

BOOL SglFrame::DecodeBlock(FILE *pFile, BLOCK *pBlock)
{
	ASSERT(pFile && pBlock);

	SGL_BLOCKHEADER sHeader;
	if (!fread(&sHeader, sizeof(sHeader), 1, pFile))
	{
		m_Error = SGL_BADFORMAT;
		return FALSE;
	}

	if (!sHeader.wWidth || !sHeader.wHeight)
	{
		TMP_ASSERT(FALSE);
		m_Error = SGL_BADFORMAT;
		return FALSE;
	}

	pBlock->eFormat = FMT_UNKNOWN;
	pBlock->wWidth = sHeader.wWidth;
	pBlock->wHeight = sHeader.wHeight;

	TMP_ASSERT(!sHeader.wReserved);

	switch (sHeader.byType)
	{
	case SGL_NULL:
		switch (sHeader.byFormat)
		{
		case SGL_EMPTYBLOCK:
			if (sHeader.dwSize)
			{
				TMP_ASSERT(FALSE);
				m_Error = SGL_BADFORMAT;
				return FALSE;
			}
			pBlock->eFormat = FMT_EMPTY;
			pBlock->dwSize = 0UL;
			return TRUE;
		default:
			break;
		}
		break;
	case SGL_RGB:
		break;
	case SGL_COMPRESSED:
		switch (sHeader.byFormat)
		{
		case SGL_DXT1BLOCK:
			pBlock->eFormat = FMT_DXT1;
			pBlock->dwSize = (pBlock->wWidth * pBlock->wHeight) / 2;
			break;
		case SGL_DXT3BLOCK:
			pBlock->eFormat = FMT_DXT3;
			pBlock->dwSize = (pBlock->wWidth * pBlock->wHeight);
			break;
		default:
			break;
		}
		break;
	}

	if (pBlock->eFormat == FMT_UNKNOWN)
	{
		TMP_ASSERT(FALSE);
		m_Error = SGL_UNKNOWNFORMAT;
		return FALSE;
	}

	if ((sHeader.wWidth & 0x3) || (sHeader.wHeight & 0x3))
	{
		TMP_ASSERT(FALSE);
		m_Error = SGL_BADFORMAT;
		return FALSE;
	}

	LPBYTE pDataBuf = new BYTE[sHeader.dwSize];
	if (!pDataBuf)
	{
		m_Error = SGL_OUTOFMEMORY;
		return FALSE;
	}

	BOOL bRet = DecodeBlock(pFile, pBlock, pDataBuf, sHeader.dwSize);

	delete [] pDataBuf;

	if (!bRet)
		return FALSE;

	return TRUE;
}

BOOL SglFrame::DecodeBlock(FILE *pFile, BLOCK *pBlock, LPBYTE pData, DWORD dwDataSize)
{
	ASSERT(pFile && pBlock && pData && dwDataSize);

	if (fread(pData, sizeof(BYTE), dwDataSize, pFile) != dwDataSize)
	{
		m_Error = SGL_BADFORMAT;
		return FALSE;
	}

	pBlock->pData = new BYTE[pBlock->dwSize];
	if (!pBlock->pData)
	{
		m_Error = SGL_OUTOFMEMORY;
		return FALSE;
	}
	memset(pBlock->pData, 0, pBlock->dwSize);

	DWORD dwSize = pBlock->dwSize;
	if (uncompress(static_cast<LPBYTE>(pBlock->pData), &dwSize, pData, dwDataSize))
	{
		m_Error = SGL_BADFORMAT;
		return FALSE;
	}

	if (dwSize != pBlock->dwSize)
	{
		m_Error = SGL_BADFORMAT;
		return FALSE;
	}

	return TRUE;
}

BOOL SglFrame::ClearBuffer(VOID)
{
	if (!m_Buffer.pBlock)
	{
		BOOL bRet = FALSE;
		if (!m_Buffer.wDataWidth && !m_Buffer.wDataHeight)
			bRet = TRUE;

		memset(&m_Buffer, 0, sizeof(m_Buffer));
		return bRet;
	}

	DWORD dwBlockNum = m_Buffer.wHBlockNum * m_Buffer.wVBlockNum;
	for (UINT i = 0U; i < dwBlockNum; i++)
		delete [] m_Buffer.pBlock[i].pData;

	delete [] m_Buffer.pBlock;
	memset(&m_Buffer, 0, sizeof(m_Buffer));

	return TRUE;
}
